package demoMod.invincibleOne.patches;

import basemod.BaseMod;
import basemod.helpers.CardModifierManager;
import basemod.interfaces.ISubscriber;
import com.badlogic.gdx.math.MathUtils;
import com.evacipated.cardcrawl.modthespire.lib.ByRef;
import com.evacipated.cardcrawl.modthespire.lib.SpireInsertPatch;
import com.evacipated.cardcrawl.modthespire.lib.SpirePatch;
import com.evacipated.cardcrawl.modthespire.lib.SpireReturn;
import com.megacrit.cardcrawl.actions.AbstractGameAction;
import com.megacrit.cardcrawl.actions.animations.VFXAction;
import com.megacrit.cardcrawl.actions.common.MakeTempCardInDrawPileAction;
import com.megacrit.cardcrawl.cards.AbstractCard;
import com.megacrit.cardcrawl.core.CardCrawlGame;
import com.megacrit.cardcrawl.core.Settings;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.dungeons.Exordium;
import com.megacrit.cardcrawl.dungeons.TheCity;
import com.megacrit.cardcrawl.random.Random;
import com.megacrit.cardcrawl.rooms.TreasureRoomBoss;
import com.megacrit.cardcrawl.saveAndContinue.SaveAndContinue;
import com.megacrit.cardcrawl.saveAndContinue.SaveFile;
import com.megacrit.cardcrawl.vfx.UpgradeShineEffect;
import com.megacrit.cardcrawl.vfx.cardManip.ShowCardAndObtainEffect;
import com.megacrit.cardcrawl.vfx.cardManip.ShowCardBrieflyEffect;
import demoMod.invincibleOne.InvincibleOneMod;
import demoMod.invincibleOne.cards.invincible.*;
import demoMod.invincibleOne.characters.InvincibleOne;
import demoMod.invincibleOne.enums.AbstractCardEnum;
import demoMod.invincibleOne.modifiers.ExhaustAtStartOfCombatMod;
import demoMod.invincibleOne.skins.Skin;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class AbstractDungeonPatch {
    @SpirePatch(
            clz = AbstractDungeon.class,
            method = "getCardFromPool"
    )
    public static class PatchGetCardFromPool {
        public static void Prefix(AbstractCard.CardRarity rarity, @ByRef(type = "cards.AbstractCard$CardType") AbstractCard.CardType[] type, boolean useRng) {
            if (AbstractDungeon.player instanceof InvincibleOne && type[0] != AbstractCard.CardType.CURSE) {
                type[0] = AbstractCard.CardType.SKILL;
            }
        }
    }

    @SpirePatch(
            clz = AbstractDungeon.class,
            method = "getColorlessCardFromPool"
    )
    public static class PatchGetColorlessCardFromPool {
        public static SpireReturn<AbstractCard> Prefix(AbstractCard.CardRarity rarity) {
            if (AbstractDungeon.player instanceof InvincibleOne) {
                return SpireReturn.Return(AbstractDungeon.getCardFromPool(AbstractDungeon.rollRarity(), AbstractCard.CardType.SKILL, true));
            }
            return SpireReturn.Continue();
        }
    }

    @SpirePatch(
            clz = AbstractDungeon.class,
            method = "rollRarity",
            paramtypez = {
                    Random.class
            }
    )
    public static class PatchRollRarity {
        public static SpireReturn<AbstractCard.CardRarity> Prefix(Random rng) {
            if (InvincibleOneMod.legendaryCardChance[CardCrawlGame.saveSlot] == 100) {
                return SpireReturn.Return(AbstractCardEnum.LEGENDARY);
            }
            return SpireReturn.Continue();
        }
    }

    @SpirePatch(
            clz = AbstractDungeon.class,
            method = "getCard",
            paramtypez = {
                    AbstractCard.CardRarity.class
            }
    )
    @SpirePatch(
            clz = AbstractDungeon.class,
            method = "getCard",
            paramtypez = {
                    AbstractCard.CardRarity.class,
                    Random.class
            }
    )
    @SpirePatch(
            clz = AbstractDungeon.class,
            method = "getCardWithoutRng",
            paramtypez = {
                    AbstractCard.CardRarity.class
            }
    )
    @SpirePatch(
            clz = AbstractDungeon.class,
            method = "getCardFromPool",
            paramtypez = {
                    AbstractCard.CardRarity.class,
                    AbstractCard.CardType.class,
                    boolean.class
            }
    )
    public static class PatchGetCard {
        public static SpireReturn<AbstractCard> Prefix(AbstractCard.CardRarity rarity) {
            if (rarity == AbstractCardEnum.LEGENDARY) {
                List<AbstractCard> legendaryCardPool = BaseMod.getCustomCardsToAdd().stream().filter(c -> c.rarity == AbstractCardEnum.LEGENDARY).collect(Collectors.toList());
                return SpireReturn.Return(legendaryCardPool.get(AbstractDungeon.cardRng.random(legendaryCardPool.size() - 1)));
            }
            return SpireReturn.Continue();
        }
    }

    @SpirePatch(
            clz = AbstractDungeon.class,
            method = "returnColorlessCard",
            paramtypez = {
                    AbstractCard.CardRarity.class
            }
    )
    public static class PatchReturnColorlessCard {
        public static SpireReturn<AbstractCard> Prefix(AbstractCard.CardRarity rarity) {
            if (AbstractDungeon.player instanceof InvincibleOne) {
                List<AbstractCard> pool = BaseMod.getCustomCardsToAdd().stream().filter(card -> card.color == AbstractCardEnum.INVINCIBLE && card.rarity != AbstractCard.CardRarity.BASIC && card.rarity != AbstractCard.CardRarity.SPECIAL).collect(Collectors.toList());
                Collections.shuffle(pool, new java.util.Random(AbstractDungeon.shuffleRng.randomLong()));
                for (AbstractCard c : pool) {
                    if (c.rarity == rarity) {
                        return SpireReturn.Return(c.makeCopy());
                    }
                }
                return SpireReturn.Return(new SwoopIn());
            }
            return SpireReturn.Continue();
        }
    }

    @SpirePatch(
            clz = AbstractDungeon.class,
            method = "isNoteForYourselfAvailable"
    )
    public static class PatchIsNoteForYourselfAvailable {
        public static SpireReturn<Boolean> Prefix(AbstractDungeon dungeon) {
            if (AbstractDungeon.player instanceof InvincibleOne) {
                return SpireReturn.Return(false);
            }
            return SpireReturn.Continue();
        }
    }

    @SpirePatch(
            clz = AbstractDungeon.class,
            method = "initializeSpecialOneTimeEventList"
    )
    public static class PatchInitializeSpecialOneTimeEventList {
        public static void Postfix(AbstractDungeon dungeon) {
            if (AbstractDungeon.player instanceof InvincibleOne) {
                AbstractDungeon.specialOneTimeEventList.remove("N'loth");
                AbstractDungeon.specialOneTimeEventList.remove("Knowing Skull");
                AbstractDungeon.specialOneTimeEventList.remove("NoteForYourself");
            }
        }
    }

    @SpirePatch(
            clz = TheCity.class,
            method = "initializeEventList"
    )
    public static class PatchInitializeEventList {
        public static void Postfix(TheCity city) {
            if (AbstractDungeon.player instanceof InvincibleOne) {
                AbstractDungeon.eventList.remove("Vampires");
            }
        }
    }

    @SpirePatch(
            clz = AbstractDungeon.class,
            method = "nextRoomTransition",
            paramtypez = {
                    SaveFile.class
            }
    )
    public static class PatchNextRoomTransition {
        @SpireInsertPatch(rloc = 1)
        public static void Insert(AbstractDungeon dungeon, SaveFile saveFile) {
            if (AbstractDungeon.nextRoom != null && !(CardCrawlGame.loadingSave && saveFile != null && saveFile.post_combat)) {
                InvincibleOneMod.bingoPanel.getSlots().forEach(slot -> slot.getBingoItem().onLeaveRoom(AbstractDungeon.getCurrRoom()));
            }
        }
    }

    @SpirePatch(
            clz = AbstractDungeon.class,
            method = "reset"
    )
    public static class PatchReset {
        public static void Prefix() {
            if (AbstractDungeon.player != null) {
                for (AbstractCard card : AbstractDungeon.player.masterDeck.group) {
                    if (card instanceof ISubscriber) {
                        BaseMod.unsubscribe((ISubscriber) card);
                    }
                    CardModifierManager.modifiers(card).removeIf(modifier -> modifier instanceof ExhaustAtStartOfCombatMod);
                }
            } else {
                System.out.println("player is null");
            }
        }
    }
}
